Load dependencies

library(tidyverse)
── Attaching packages ───────────────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 2.2.1     ✔ purrr   0.2.4
✔ tibble  1.4.2     ✔ dplyr   0.7.4
✔ tidyr   0.8.0     ✔ stringr 1.3.0
✔ readr   1.1.1     ✔ forcats 0.3.0
── Conflicts ──────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
Error in names(frame) <- `*vtmp*` : names() applied to a non-vector
library(cowplot)

Attaching package: ‘cowplot’

The following object is masked from ‘package:ggplot2’:

    ggsave

1 Reading & Cleaning

Define where the data lives

## Define data paths
in_path_varscan = 'data/maf/6c93f518-1956-4435-9806-37185266d248/TCGA.BRCA.varscan.6c93f518-1956-4435-9806-37185266d248.DR-10.0.somatic.maf.gz'
in_path_muse = 'data/maf/b8ca5856-9819-459c-87c5-94e91aca4032/TCGA.BRCA.muse.b8ca5856-9819-459c-87c5-94e91aca4032.DR-10.0.somatic.maf.gz'
in_path_ss = 'data/maf/7dd592e3-5950-4438-96d5-3c718aca3f13/TCGA.BRCA.somaticsniper.7dd592e3-5950-4438-96d5-3c718aca3f13.DR-10.0.somatic.maf.gz'
in_path_mutect = 'data/maf/995c0111-d90b-4140-bee7-3845436c3b42/TCGA.BRCA.mutect.995c0111-d90b-4140-bee7-3845436c3b42.DR-10.0.somatic.maf.gz'

Reading data

df = read_tsv(
  in_path_mutect,
  comment = '#'
)

Preview data

print(dim(df))
[1] 120988    120
head(df, 10)

Read & concatenate multiple files

## Define column types
col_types = cols_only(
  Chromosome = 'c',
  Start_Position = 'i',
  End_Position = 'i',
  SYMBOL = 'c',
  Reference_Allele = 'c',
  Allele = 'c', 
  Variant_Classification = 'c', 
  IMPACT = 'c',
  Variant_Type = 'c',
  Tumor_Sample_Barcode = 'c'
)
## Concatenate vertically
in_paths = c(
  in_path_varscan,
  in_path_muse,
  in_path_ss,
  in_path_mutect
)
df = bind_rows(lapply(
  in_paths, 
  read_tsv, 
  col_types = col_types,
  comment = '#'
))
## Convert characters to factors
# Note, if we weren't concatenating,
# this could be done while reading 
# by specifying 
# `COL_NAME = col_factor(levels=NULL)`
# inside cols_only()
df = mutate_if(
  df,
  is.character,
  as.factor
)
print(dim(df))
[1] 370461     10
head(df)

Rename and reorder columns

## Rename
col_name_map = list(
  CHR = 'Chromosome',
  START = 'Start_Position',
  END = 'End_Position',
  GENE = 'SYMBOL',
  REF = 'Reference_Allele',
  ALT = 'Allele',
  CLASS = 'Variant_Classification',
  IMPACT = 'IMPACT',
  TYPE = 'Variant_Type',
  BARCODE = 'Tumor_Sample_Barcode'
)
df = rename(df, !!! col_name_map)
## Reorder
keep_cols = names(col_name_map)
df = select(df, keep_cols)
head(df)

Fill missing data

levels(df$GENE) = c(levels(df$GENE), 'INTERGENIC')
df$GENE[is.na(df$GENE)] = 'INTERGENIC'

Create new columns from string ops

## New columns by assignment
df$SAMPLE = str_sub(df$BARCODE, 1, 12)
df$MUTATION = paste(
  df$CHR, 
  df$GENE,
  df$START,
  df$END,
  df$REF,
  df$ALT,
  sep = ':'
)
df$TYPE2 = ifelse(df$TYPE == 'SNP', 'SNP', 'INDEL')
## New columns with mutate()
# Equivalent to above (and thus redundant)
df = mutate(
  df,
  SAMPLE = str_sub(BARCODE, 1, 12),
  MUTATION = paste(
    CHR,
    GENE,
    START,
    END,
    REF,
    ALT,
    sep = ':'
  ),
  TYPE2 = ifelse(TYPE == 'SNP', 'SNP', 'INDEL')
)
## Convert new columns to factors if applicable,
## possibly with manually-set levels
df$SAMPLE = as.factor(df$SAMPLE)
df$MUTATION = as.factor(df$MUTATION)
df$TYPE2 = factor(df$TYPE2, levels = c('SNP', 'INDEL'))
print(dim(df))
[1] 370461     13
head(df)

Remove duplicates

## Remove duplicate rows
df = unique(df)
## Remove duplicate based on column
df_mut = df[!duplicated(df$MUTATION),]
print(dim(df))
[1] 132916     13
print(dim(df_mut))
[1] 130625     13

Remove columns

drop_cols = c('BARCODE', 'SAMPLE')
df_mut = select(df_mut, -one_of(drop_cols))
print(dim(df_mut))
[1] 130625     11
head(df_mut)

New columns from delimited column

## Create dummy frame
df_tmp = df
drop_cols = c(
    'CHR',
    'GENE',
    'START',
    'END',
    'REF',
    'ALT'
)
df_tmp = select(df_tmp, -one_of(drop_cols))
## Create new columns
df_tmp = separate(
  df_tmp,
  MUTATION,
  drop_cols,
  ':'
)
print(dim(df_tmp))
[1] 132916     12
head(df_tmp)

Writing files

write_delim(
  df, 
  'data/out/df_r.tsv', 
  delim = '\t'
)

Read & write binary files

rds_path = 'data/out/df.rds'
## Write
saveRDS(df, rds_path)
## Read
df = readRDS(rds_path)

Long to wide

## Shorten to relevant columns
# Note, we could include other columns here
# like GENE and IMPACT, but we won't 
# to match the python example.
## Create a dummy column w/ fill value
df_tmp = select(df, c('SAMPLE', 'MUTATION'))
df_tmp$EXISTS = 1
## Long-to-wide
df_wide = spread(
  df_tmp,
  key = 'SAMPLE',
  value = 'EXISTS',
  fill = 0
)
rm(df_tmp)
print(dim(df_wide))
[1] 130625    987
head(df_wide)

Merge

## Select columns to add on and col to join on
cols = c('MUTATION', 'GENE', 'IMPACT')
## Merge
df_wide = merge(
  df_wide, select(df_mut, cols),
  by = 'MUTATION',
  all.x = TRUE
)
## Re-order columns
# Put new columns first
df_wide = select(df_wide, cols, everything())
# Note, first 3 columns are out of order 
# w/ python example.
print(dim(df_wide))
[1] 130625    989
head(df_wide)

Wide to long

# Set value and id columns
value_cols = grep('TCGA-', names(df_wide), value = TRUE)
# Melt
df_long = gather(
  df_wide,
  key = 'SAMPLE',
  value = 'EXISTS',
  value_cols
)
# Clean up
df_long = filter(df_long, EXISTS != 0)
df_long = select(df_long, -EXISTS)
print(dim(df_long))
[1] 132916      4
head(df_long)

Free memory

rm(df_wide, df_long)

Counting

Count unique elements

n_genes = length(unique(df$GENE))
n_samples = length(unique(df$SAMPLE))
n_mutations = length(unique(df$MUTATION))
n_mutation_classes = length(unique(df$CLASS))
print('Number of unique')
[1] "Number of unique"
cat('\tSamples: ', n_samples, '\n')
    Samples:  986 
cat('\tGenes: ', n_genes, '\n')
    Genes:  19168 
cat('\tMutations: ', n_mutations, '\n')
    Mutations:  130625 
cat('\tMutation Classes: ', n_mutation_classes, '\n')
    Mutation Classes:  18 

Count by factor levels

class_counts = sort(summary(df$CLASS), decreasing = TRUE)
impact_counts = sort(summary(df$IMPACT), decreasing = TRUE)
type_counts = sort(summary(df$TYPE), decreasing = TRUE)
print('Counts per CLASS:')
[1] "Counts per CLASS:"
print(class_counts)
     Missense_Mutation                 Silent                  3'UTR                 Intron 
                 66371                  23881                  11052                   6990 
     Nonsense_Mutation        Frame_Shift_Del                  5'UTR                    RNA 
                  6056                   3661                   3392                   2543 
       Frame_Shift_Ins            Splice_Site          Splice_Region                3'Flank 
                  1975                   1921                   1537                   1132 
               5'Flank           In_Frame_Del           In_Frame_Ins       Nonstop_Mutation 
                   906                    869                    433                     93 
Translation_Start_Site                    IGR 
                    90                     14 
print('Counts per IMPACT:')
[1] "Counts per IMPACT:"
print(impact_counts)
MODERATE MODIFIER      LOW     HIGH 
   67648    26059    25413    13796 
print('Counts per TYPE:')
[1] "Counts per TYPE:"
print(type_counts)
   SNP    DEL    INS 
121319   7316   4281 
## Display some instances
# Need to override the maxsum = 50 arg
mut_counts =  sort(
  summary(df$MUTATION, maxsum = n_mutations), 
  decreasing = TRUE)
print('Top repeated (>10) mutations:')
[1] "Top repeated (>10) mutations:"
print(mut_counts[mut_counts > 10])
     chr3:PIK3CA:179234297:179234297:A:G      chr3:PIK3CA:179218303:179218303:G:A 
                                     121                                       63 
     chr3:PIK3CA:179218294:179218294:G:A chr1:ST6GALNAC3:76576946:76576947:-:AAAC 
                                      43                                       33 
      chr14:AKT1:104780214:104780214:C:T         chr10:GATA3:8069470:8069471:CA:- 
                                      25                                       21 
       chr3:MUC4:195783009:195783009:C:T           chr17:TP53:7675088:7675088:C:T 
                                      21                                       20 
       chr3:MUC4:195783008:195783008:A:G     chr15:GOLGA6L6:20535018:20535018:C:T 
                                      20                                       17 
     chr3:PIK3CA:179203765:179203765:T:A      chr3:PIK3CA:179234297:179234297:A:T 
                                      17                                       13 
 chr6:OPRM1:154107953:154107958:TTTTTA:-           chr17:TP53:7673802:7673802:C:T 
                                      13                                       12 
      chr16:PKD1P6:15104542:15104542:T:A      chr1:NBPF12:146963189:146963189:G:C 
                                      11                                       11 
sample_counts =  sort(
  summary(df$SAMPLE, maxsum = n_samples), 
  decreasing = TRUE)
print('Samples with most mutations:')
[1] "Samples with most mutations:"
print(sample_counts[0:10])
TCGA-AN-A046 TCGA-AC-A23H TCGA-5L-AAT1 TCGA-BH-A18G TCGA-AN-A0AK TCGA-A8-A09Z TCGA-BH-A0HF 
        7948         6711         2117         2033         2029         1924         1695 
TCGA-AO-A128 TCGA-D8-A1XK TCGA-BH-A0B6 
        1644         1541         1419 
print('Samples with least mutations:')
[1] "Samples with least mutations:"
print(tail(sample_counts, 10))
TCGA-A2-A0ES TCGA-AC-A2FB TCGA-AR-A24W TCGA-AR-A252 TCGA-LL-A440 TCGA-AO-A1KO TCGA-A2-A1G6 
          18           17           16           16           16           15           12 
TCGA-A8-A08C TCGA-A2-A25F TCGA-AC-A2FK 
           9            7            2 

Summarize by group

df_summary = df %>%
  group_by(GENE) %>%
  summarise(
    N_MUTATIONS = n(),
    N_UNIQUE_MUTATIONS = n_distinct(MUTATION),
    N_SAMPLES = n_distinct(BARCODE)
)
head(df_summary)

Sorting

df_summary = df_summary %>%
  arrange(desc(N_SAMPLES), N_MUTATIONS)
head(df_summary)

Summarize by multiple groups

df_impact = df %>%
  group_by(GENE, IMPACT) %>%
  summarise(COUNT = n_distinct(MUTATION))
print(dim(df_impact))
[1] 46692     3
head(df_impact)

Descriptive statistics

## Make counts per group
df_sample_counts = df %>%
  group_by(SAMPLE, TYPE2, IMPACT) %>%
  summarize(N_MUTATIONS = n()) %>%
  filter(N_MUTATIONS >= 20, N_MUTATIONS <= 200)
## Calculate stats
df_sample_stats = df_sample_counts %>%
  group_by(TYPE2, IMPACT) %>%
  summarize(
    MEDIAN = median(N_MUTATIONS),
    UPPER = quantile(N_MUTATIONS, .75),
    LOWER = quantile(N_MUTATIONS, .25),
    MEAN = mean(N_MUTATIONS),
    STD = sd(N_MUTATIONS),
    MIN = min(N_MUTATIONS),
    MAX = max(N_MUTATIONS)
  )
head(df_sample_counts)
df_sample_stats

Visualizations

Use cowplot for nice theme and subplotting https://cran.r-project.org/web/packages/cowplot/vignettes/introduction.html

img_dir = 'img/'

Ranking

NOTE: shape = ‘4’ corresponds to X’s

x = 0:(n_samples-1)
y = unname(sample_counts)
p = ggplot(mapping = aes(x, y)) +
  geom_line() +
  geom_point(shape = 4) +
  ggtitle('Mutations per Individual') +
  xlab('Individual Rank') +
  ylab('Number of Mutations')
p
ggsave(paste0(img_dir, 'rank_r.png')) 
Saving 5.28 x 3.26 in image

Bar plot

p = ggplot(
    mapping = aes(names(class_counts), unname(class_counts))
  ) +
  geom_bar(stat = 'identity') +
  scale_x_discrete(limits = rev(names(class_counts))) +
  coord_flip()
p
ggsave(paste0(img_dir, 'bar_r.png'))
Saving 5.28 x 3.26 in image

Subplots

## First plot
# Reuse instance from above
p1 = p
## Second plot
n_genes_to_plot = 5
genes = df_summary$GENE[0:n_genes_to_plot]
counts = df_summary$N_SAMPLES[0:n_genes_to_plot]
p2 = ggplot(mapping = aes(genes, counts)) +
  geom_bar(stat = 'identity') +
  scale_x_discrete(limits = genes)
  
p_grid = plot_grid(p1, p2, ncol = 2)
save_plot(paste0(img_dir, 'subplot_r.png'), p_grid, ncol = 2)
p_grid

Count vs identity

## New df's for plotting
df_identity = df_summary %>%
  arrange(-N_MUTATIONS)
df_count = df
## Plot params
n_genes_to_plot = 5
genes = df_identity$GENE[0:n_genes_to_plot]
counts = df_identity$N_MUTATIONS[0:n_genes_to_plot]
## Identity plot
p1 = ggplot(mapping = aes(genes, counts)) +
  geom_bar(stat = 'identity') + 
  scale_x_discrete(limits = genes) +
  ggtitle('# Mutations - Identity')
## Count plot
p2 = ggplot(df, aes(GENE)) +
  geom_bar() +
  scale_x_discrete(limits = genes) + 
  ggtitle('# Mutations - Count')
p_grid = plot_grid(p1, p2, ncol = 2)
Removed 131195 rows containing non-finite values (stat_count).
save_plot(paste0(img_dir, 'count_vs_identity_r.png'), p_grid, ncol = 2)
p_grid

Plot with grouping

## Plot params
n_genes_to_plot = 5
genes = df_summary$GENE[0:n_genes_to_plot]
## Filter to relvant data
df_plt = df_impact %>%
  filter(GENE %in% genes)
## Reorder for plot
# This is an alternative to 
# scale_x_discrete(limits=genes)
df_plt$GENE = factor(df_plt$GENE, levels = genes)
p = ggplot(data = df_plt) +
  geom_bar(
    aes(x=GENE, y=COUNT, fill=IMPACT),
    stat = 'identity',
    position = 'dodge'
  ) +
  scale_fill_brewer(
    type='qual',
    palette = 'Set1'
  ) +
  ggtitle('Impact of Mutations')
p
ggsave(paste0(img_dir, 'group_r.png'), p)
Saving 5.28 x 3.26 in image

Faceted plot

## Get some samples and genes
samples = names(sample_counts[0:2])
genes = c('PIK3CA', 'TTN', 'MUC16')
## Filter to relevant data
df_plt = df %>%
  filter(SAMPLE %in% samples & GENE %in% genes)
## Reorder
df_plt$GENE = factor(df_plt$GENE, levels = genes)
df_plt$SAMPLE = factor(df_plt$SAMPLE, levels = samples)
## Plot
p = ggplot(df_plt) +
  geom_bar(
    aes(x=GENE, fill=IMPACT),
    position = 'dodge'
  ) + 
  scale_fill_brewer(
    type='qual',
    palette = 'Set1'
  ) +
  facet_wrap('SAMPLE')
p
ggsave(paste0(img_dir, 'facet_r.png'), p)
Saving 5.28 x 3.26 in image

Box plot

## Set order
df_sample_counts$TYPE2 = factor(df_sample_counts$TYPE2, levels = c('INDEL', 'SNP'))
df_sample_counts$IMPACT = factor(df_sample_counts$IMPACT, levels = c(
  'HIGH', 'MODERATE', 'LOW', 'MODIFIER'))
## Box plot
p = ggplot(df_sample_counts, aes(TYPE2, N_MUTATIONS, fill = IMPACT)) +
  geom_boxplot() +
  coord_flip() +
  scale_fill_brewer(
    type='qual',
    palette = 'Set1'
  ) +
  ggtitle('Mutations Per Sample') +
  xlab('Type')
p
ggsave(paste0(img_dir, 'box_group_r.png'), p)
Saving 5.28 x 3.26 in image

LS0tCnRpdGxlOiAiRXhwbG9yaW5nIFRDR0EiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIExvYWQgZGVwZW5kZW5jaWVzCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShjb3dwbG90KQpgYGAKCiMgMSBSZWFkaW5nICYgQ2xlYW5pbmcKCiMjIERlZmluZSB3aGVyZSB0aGUgZGF0YSBsaXZlcwpgYGB7cn0KIyMgRGVmaW5lIGRhdGEgcGF0aHMKaW5fcGF0aF92YXJzY2FuID0gJ2RhdGEvbWFmLzZjOTNmNTE4LTE5NTYtNDQzNS05ODA2LTM3MTg1MjY2ZDI0OC9UQ0dBLkJSQ0EudmFyc2Nhbi42YzkzZjUxOC0xOTU2LTQ0MzUtOTgwNi0zNzE4NTI2NmQyNDguRFItMTAuMC5zb21hdGljLm1hZi5neicKaW5fcGF0aF9tdXNlID0gJ2RhdGEvbWFmL2I4Y2E1ODU2LTk4MTktNDU5Yy04N2M1LTk0ZTkxYWNhNDAzMi9UQ0dBLkJSQ0EubXVzZS5iOGNhNTg1Ni05ODE5LTQ1OWMtODdjNS05NGU5MWFjYTQwMzIuRFItMTAuMC5zb21hdGljLm1hZi5neicKaW5fcGF0aF9zcyA9ICdkYXRhL21hZi83ZGQ1OTJlMy01OTUwLTQ0MzgtOTZkNS0zYzcxOGFjYTNmMTMvVENHQS5CUkNBLnNvbWF0aWNzbmlwZXIuN2RkNTkyZTMtNTk1MC00NDM4LTk2ZDUtM2M3MThhY2EzZjEzLkRSLTEwLjAuc29tYXRpYy5tYWYuZ3onCmluX3BhdGhfbXV0ZWN0ID0gJ2RhdGEvbWFmLzk5NWMwMTExLWQ5MGItNDE0MC1iZWU3LTM4NDU0MzZjM2I0Mi9UQ0dBLkJSQ0EubXV0ZWN0Ljk5NWMwMTExLWQ5MGItNDE0MC1iZWU3LTM4NDU0MzZjM2I0Mi5EUi0xMC4wLnNvbWF0aWMubWFmLmd6JwpgYGAKCiMjIFJlYWRpbmcgZGF0YQpgYGB7ciBldmFsPUZBTFNFfQpkZiA9IHJlYWRfdHN2KAogIGluX3BhdGhfbXV0ZWN0LAogIGNvbW1lbnQgPSAnIycKKQpgYGAKCgojIyBQcmV2aWV3IGRhdGEKYGBge3J9CnByaW50KGRpbShkZikpCmhlYWQoZGYsIDEwKQpgYGAKCgojIyBSZWFkICYgY29uY2F0ZW5hdGUgbXVsdGlwbGUgZmlsZXMKYGBge3J9CiMjIERlZmluZSBjb2x1bW4gdHlwZXMKY29sX3R5cGVzID0gY29sc19vbmx5KAogIENocm9tb3NvbWUgPSAnYycsCiAgU3RhcnRfUG9zaXRpb24gPSAnaScsCiAgRW5kX1Bvc2l0aW9uID0gJ2knLAogIFNZTUJPTCA9ICdjJywKICBSZWZlcmVuY2VfQWxsZWxlID0gJ2MnLAogIEFsbGVsZSA9ICdjJywgCiAgVmFyaWFudF9DbGFzc2lmaWNhdGlvbiA9ICdjJywgCiAgSU1QQUNUID0gJ2MnLAogIFZhcmlhbnRfVHlwZSA9ICdjJywKICBUdW1vcl9TYW1wbGVfQmFyY29kZSA9ICdjJwopCgojIyBDb25jYXRlbmF0ZSB2ZXJ0aWNhbGx5CmluX3BhdGhzID0gYygKICBpbl9wYXRoX3ZhcnNjYW4sCiAgaW5fcGF0aF9tdXNlLAogIGluX3BhdGhfc3MsCiAgaW5fcGF0aF9tdXRlY3QKKQpkZiA9IGJpbmRfcm93cyhsYXBwbHkoCiAgaW5fcGF0aHMsIAogIHJlYWRfdHN2LCAKICBjb2xfdHlwZXMgPSBjb2xfdHlwZXMsCiAgY29tbWVudCA9ICcjJwopKQoKIyMgQ29udmVydCBjaGFyYWN0ZXJzIHRvIGZhY3RvcnMKIyBOb3RlLCBpZiB3ZSB3ZXJlbid0IGNvbmNhdGVuYXRpbmcsCiMgdGhpcyBjb3VsZCBiZSBkb25lIHdoaWxlIHJlYWRpbmcgCiMgYnkgc3BlY2lmeWluZyAKIyBgQ09MX05BTUUgPSBjb2xfZmFjdG9yKGxldmVscz1OVUxMKWAKIyBpbnNpZGUgY29sc19vbmx5KCkKZGYgPSBtdXRhdGVfaWYoCiAgZGYsCiAgaXMuY2hhcmFjdGVyLAogIGFzLmZhY3RvcgopCgpwcmludChkaW0oZGYpKQpoZWFkKGRmKQpgYGAKCiMjIFJlbmFtZSBhbmQgcmVvcmRlciBjb2x1bW5zCmBgYHtyfQojIyBSZW5hbWUKY29sX25hbWVfbWFwID0gbGlzdCgKICBDSFIgPSAnQ2hyb21vc29tZScsCiAgU1RBUlQgPSAnU3RhcnRfUG9zaXRpb24nLAogIEVORCA9ICdFbmRfUG9zaXRpb24nLAogIEdFTkUgPSAnU1lNQk9MJywKICBSRUYgPSAnUmVmZXJlbmNlX0FsbGVsZScsCiAgQUxUID0gJ0FsbGVsZScsCiAgQ0xBU1MgPSAnVmFyaWFudF9DbGFzc2lmaWNhdGlvbicsCiAgSU1QQUNUID0gJ0lNUEFDVCcsCiAgVFlQRSA9ICdWYXJpYW50X1R5cGUnLAogIEJBUkNPREUgPSAnVHVtb3JfU2FtcGxlX0JhcmNvZGUnCikKZGYgPSByZW5hbWUoZGYsICEhISBjb2xfbmFtZV9tYXApCgojIyBSZW9yZGVyCmtlZXBfY29scyA9IG5hbWVzKGNvbF9uYW1lX21hcCkKZGYgPSBzZWxlY3QoZGYsIGtlZXBfY29scykKCmhlYWQoZGYpCmBgYAoKIyMgRmlsbCBtaXNzaW5nIGRhdGEKYGBge3J9CmxldmVscyhkZiRHRU5FKSA9IGMobGV2ZWxzKGRmJEdFTkUpLCAnSU5URVJHRU5JQycpCmRmJEdFTkVbaXMubmEoZGYkR0VORSldID0gJ0lOVEVSR0VOSUMnCmBgYAoKIyMgQ3JlYXRlIG5ldyBjb2x1bW5zIGZyb20gc3RyaW5nIG9wcwpgYGB7cn0KCiMjIE5ldyBjb2x1bW5zIGJ5IGFzc2lnbm1lbnQKZGYkU0FNUExFID0gc3RyX3N1YihkZiRCQVJDT0RFLCAxLCAxMikKZGYkTVVUQVRJT04gPSBwYXN0ZSgKICBkZiRDSFIsIAogIGRmJEdFTkUsCiAgZGYkU1RBUlQsCiAgZGYkRU5ELAogIGRmJFJFRiwKICBkZiRBTFQsCiAgc2VwID0gJzonCikKZGYkVFlQRTIgPSBpZmVsc2UoZGYkVFlQRSA9PSAnU05QJywgJ1NOUCcsICdJTkRFTCcpCgojIyBOZXcgY29sdW1ucyB3aXRoIG11dGF0ZSgpCiMgRXF1aXZhbGVudCB0byBhYm92ZSAoYW5kIHRodXMgcmVkdW5kYW50KQpkZiA9IG11dGF0ZSgKICBkZiwKICBTQU1QTEUgPSBzdHJfc3ViKEJBUkNPREUsIDEsIDEyKSwKICBNVVRBVElPTiA9IHBhc3RlKAogICAgQ0hSLAogICAgR0VORSwKICAgIFNUQVJULAogICAgRU5ELAogICAgUkVGLAogICAgQUxULAogICAgc2VwID0gJzonCiAgKSwKICBUWVBFMiA9IGlmZWxzZShUWVBFID09ICdTTlAnLCAnU05QJywgJ0lOREVMJykKKQoKIyMgQ29udmVydCBuZXcgY29sdW1ucyB0byBmYWN0b3JzIGlmIGFwcGxpY2FibGUsCiMjIHBvc3NpYmx5IHdpdGggbWFudWFsbHktc2V0IGxldmVscwpkZiRTQU1QTEUgPSBhcy5mYWN0b3IoZGYkU0FNUExFKQpkZiRNVVRBVElPTiA9IGFzLmZhY3RvcihkZiRNVVRBVElPTikKZGYkVFlQRTIgPSBmYWN0b3IoZGYkVFlQRTIsIGxldmVscyA9IGMoJ1NOUCcsICdJTkRFTCcpKQoKcHJpbnQoZGltKGRmKSkKaGVhZChkZikKYGBgCgojIyBSZW1vdmUgZHVwbGljYXRlcwpgYGB7cn0KCiMjIFJlbW92ZSBkdXBsaWNhdGUgcm93cwpkZiA9IHVuaXF1ZShkZikKCiMjIFJlbW92ZSBkdXBsaWNhdGUgYmFzZWQgb24gY29sdW1uCmRmX211dCA9IGRmWyFkdXBsaWNhdGVkKGRmJE1VVEFUSU9OKSxdCgpwcmludChkaW0oZGYpKQpwcmludChkaW0oZGZfbXV0KSkKYGBgCgojIyBSZW1vdmUgY29sdW1ucwpgYGB7cn0KZHJvcF9jb2xzID0gYygnQkFSQ09ERScsICdTQU1QTEUnKQpkZl9tdXQgPSBzZWxlY3QoZGZfbXV0LCAtb25lX29mKGRyb3BfY29scykpCgpwcmludChkaW0oZGZfbXV0KSkKaGVhZChkZl9tdXQpCmBgYAoKIyMgTmV3IGNvbHVtbnMgZnJvbSBkZWxpbWl0ZWQgY29sdW1uCmBgYHtyfQoKIyMgQ3JlYXRlIGR1bW15IGZyYW1lCmRmX3RtcCA9IGRmCmRyb3BfY29scyA9IGMoCiAgICAnQ0hSJywKICAgICdHRU5FJywKICAgICdTVEFSVCcsCiAgICAnRU5EJywKICAgICdSRUYnLAogICAgJ0FMVCcKKQpkZl90bXAgPSBzZWxlY3QoZGZfdG1wLCAtb25lX29mKGRyb3BfY29scykpCgojIyBDcmVhdGUgbmV3IGNvbHVtbnMKZGZfdG1wID0gc2VwYXJhdGUoCiAgZGZfdG1wLAogIE1VVEFUSU9OLAogIGRyb3BfY29scywKICAnOicKKQoKcHJpbnQoZGltKGRmX3RtcCkpCmhlYWQoZGZfdG1wKQpgYGAKCiMjIFdyaXRpbmcgZmlsZXMKYGBge3J9CndyaXRlX2RlbGltKAogIGRmLCAKICAnZGF0YS9vdXQvZGZfci50c3YnLCAKICBkZWxpbSA9ICdcdCcKKQpgYGAKCgojIyBSZWFkICYgd3JpdGUgYmluYXJ5IGZpbGVzCmBgYHtyfQoKcmRzX3BhdGggPSAnZGF0YS9vdXQvZGYucmRzJwoKIyMgV3JpdGUKc2F2ZVJEUyhkZiwgcmRzX3BhdGgpCgojIyBSZWFkCmRmID0gcmVhZFJEUyhyZHNfcGF0aCkKYGBgCgojIyBMb25nIHRvIHdpZGUKYGBge3J9CgojIyBTaG9ydGVuIHRvIHJlbGV2YW50IGNvbHVtbnMKIyBOb3RlLCB3ZSBjb3VsZCBpbmNsdWRlIG90aGVyIGNvbHVtbnMgaGVyZQojIGxpa2UgR0VORSBhbmQgSU1QQUNULCBidXQgd2Ugd29uJ3QgCiMgdG8gbWF0Y2ggdGhlIHB5dGhvbiBleGFtcGxlLgoKIyMgQ3JlYXRlIGEgZHVtbXkgY29sdW1uIHcvIGZpbGwgdmFsdWUKZGZfdG1wID0gc2VsZWN0KGRmLCBjKCdTQU1QTEUnLCAnTVVUQVRJT04nKSkKZGZfdG1wJEVYSVNUUyA9IDEKCiMjIExvbmctdG8td2lkZQpkZl93aWRlID0gc3ByZWFkKAogIGRmX3RtcCwKICBrZXkgPSAnU0FNUExFJywKICB2YWx1ZSA9ICdFWElTVFMnLAogIGZpbGwgPSAwCikKcm0oZGZfdG1wKQoKcHJpbnQoZGltKGRmX3dpZGUpKQpoZWFkKGRmX3dpZGUpCgpgYGAKCiMjIE1lcmdlCmBgYHtyfQoKIyMgU2VsZWN0IGNvbHVtbnMgdG8gYWRkIG9uIGFuZCBjb2wgdG8gam9pbiBvbgpjb2xzID0gYygnTVVUQVRJT04nLCAnR0VORScsICdJTVBBQ1QnKQoKIyMgTWVyZ2UKZGZfd2lkZSA9IG1lcmdlKAogIGRmX3dpZGUsIHNlbGVjdChkZl9tdXQsIGNvbHMpLAogIGJ5ID0gJ01VVEFUSU9OJywKICBhbGwueCA9IFRSVUUKKQoKIyMgUmUtb3JkZXIgY29sdW1ucwojIFB1dCBuZXcgY29sdW1ucyBmaXJzdApkZl93aWRlID0gc2VsZWN0KGRmX3dpZGUsIGNvbHMsIGV2ZXJ5dGhpbmcoKSkKCiMgTm90ZSwgZmlyc3QgMyBjb2x1bW5zIGFyZSBvdXQgb2Ygb3JkZXIgCiMgdy8gcHl0aG9uIGV4YW1wbGUuCnByaW50KGRpbShkZl93aWRlKSkKaGVhZChkZl93aWRlKQpgYGAKCiMjIFdpZGUgdG8gbG9uZwpgYGB7cn0KCiMgU2V0IHZhbHVlIGFuZCBpZCBjb2x1bW5zCnZhbHVlX2NvbHMgPSBncmVwKCdUQ0dBLScsIG5hbWVzKGRmX3dpZGUpLCB2YWx1ZSA9IFRSVUUpCgojIE1lbHQKZGZfbG9uZyA9IGdhdGhlcigKICBkZl93aWRlLAogIGtleSA9ICdTQU1QTEUnLAogIHZhbHVlID0gJ0VYSVNUUycsCiAgdmFsdWVfY29scwopCgojIENsZWFuIHVwCmRmX2xvbmcgPSBmaWx0ZXIoZGZfbG9uZywgRVhJU1RTICE9IDApCmRmX2xvbmcgPSBzZWxlY3QoZGZfbG9uZywgLUVYSVNUUykKCnByaW50KGRpbShkZl9sb25nKSkKaGVhZChkZl9sb25nKQpgYGAKCiMjIEZyZWUgbWVtb3J5CmBgYHtyfQpybShkZl93aWRlLCBkZl9sb25nKQpgYGAKCiMgQ291bnRpbmcgCgojIyBDb3VudCB1bmlxdWUgZWxlbWVudHMKYGBge3J9Cm5fZ2VuZXMgPSBsZW5ndGgodW5pcXVlKGRmJEdFTkUpKQpuX3NhbXBsZXMgPSBsZW5ndGgodW5pcXVlKGRmJFNBTVBMRSkpCm5fbXV0YXRpb25zID0gbGVuZ3RoKHVuaXF1ZShkZiRNVVRBVElPTikpCm5fbXV0YXRpb25fY2xhc3NlcyA9IGxlbmd0aCh1bmlxdWUoZGYkQ0xBU1MpKQoKCnByaW50KCdOdW1iZXIgb2YgdW5pcXVlJykKY2F0KCdcdFNhbXBsZXM6ICcsIG5fc2FtcGxlcywgJ1xuJykKY2F0KCdcdEdlbmVzOiAnLCBuX2dlbmVzLCAnXG4nKQpjYXQoJ1x0TXV0YXRpb25zOiAnLCBuX211dGF0aW9ucywgJ1xuJykKY2F0KCdcdE11dGF0aW9uIENsYXNzZXM6ICcsIG5fbXV0YXRpb25fY2xhc3NlcywgJ1xuJykKYGBgCgojIyBDb3VudCBieSBmYWN0b3IgbGV2ZWxzCmBgYHtyfQpjbGFzc19jb3VudHMgPSBzb3J0KHN1bW1hcnkoZGYkQ0xBU1MpLCBkZWNyZWFzaW5nID0gVFJVRSkKaW1wYWN0X2NvdW50cyA9IHNvcnQoc3VtbWFyeShkZiRJTVBBQ1QpLCBkZWNyZWFzaW5nID0gVFJVRSkKdHlwZV9jb3VudHMgPSBzb3J0KHN1bW1hcnkoZGYkVFlQRSksIGRlY3JlYXNpbmcgPSBUUlVFKQoKcHJpbnQoJ0NvdW50cyBwZXIgQ0xBU1M6JykKcHJpbnQoY2xhc3NfY291bnRzKQpwcmludCgnQ291bnRzIHBlciBJTVBBQ1Q6JykKcHJpbnQoaW1wYWN0X2NvdW50cykKcHJpbnQoJ0NvdW50cyBwZXIgVFlQRTonKQpwcmludCh0eXBlX2NvdW50cykKCiMjIERpc3BsYXkgc29tZSBpbnN0YW5jZXMKIyBOZWVkIHRvIG92ZXJyaWRlIHRoZSBtYXhzdW0gPSA1MCBhcmcKbXV0X2NvdW50cyA9ICBzb3J0KAogIHN1bW1hcnkoZGYkTVVUQVRJT04sIG1heHN1bSA9IG5fbXV0YXRpb25zKSwgCiAgZGVjcmVhc2luZyA9IFRSVUUpCnByaW50KCdUb3AgcmVwZWF0ZWQgKD4xMCkgbXV0YXRpb25zOicpCnByaW50KG11dF9jb3VudHNbbXV0X2NvdW50cyA+IDEwXSkKCnNhbXBsZV9jb3VudHMgPSAgc29ydCgKICBzdW1tYXJ5KGRmJFNBTVBMRSwgbWF4c3VtID0gbl9zYW1wbGVzKSwgCiAgZGVjcmVhc2luZyA9IFRSVUUpCnByaW50KCdTYW1wbGVzIHdpdGggbW9zdCBtdXRhdGlvbnM6JykKcHJpbnQoc2FtcGxlX2NvdW50c1swOjEwXSkKcHJpbnQoJ1NhbXBsZXMgd2l0aCBsZWFzdCBtdXRhdGlvbnM6JykKcHJpbnQodGFpbChzYW1wbGVfY291bnRzLCAxMCkpCmBgYAoKCiMjIFN1bW1hcml6ZSBieSBncm91cApgYGB7cn0KZGZfc3VtbWFyeSA9IGRmICU+JQogIGdyb3VwX2J5KEdFTkUpICU+JQogIHN1bW1hcmlzZSgKICAgIE5fTVVUQVRJT05TID0gbigpLAogICAgTl9VTklRVUVfTVVUQVRJT05TID0gbl9kaXN0aW5jdChNVVRBVElPTiksCiAgICBOX1NBTVBMRVMgPSBuX2Rpc3RpbmN0KEJBUkNPREUpCikKaGVhZChkZl9zdW1tYXJ5KQpgYGAKCiMjIFNvcnRpbmcKYGBge3J9CmRmX3N1bW1hcnkgPSBkZl9zdW1tYXJ5ICU+JQogIGFycmFuZ2UoZGVzYyhOX1NBTVBMRVMpLCBOX01VVEFUSU9OUykKaGVhZChkZl9zdW1tYXJ5KQpgYGAKCgojIyBTdW1tYXJpemUgYnkgbXVsdGlwbGUgZ3JvdXBzCmBgYHtyfQpkZl9pbXBhY3QgPSBkZiAlPiUKICBncm91cF9ieShHRU5FLCBJTVBBQ1QpICU+JQogIHN1bW1hcmlzZShDT1VOVCA9IG5fZGlzdGluY3QoTVVUQVRJT04pKQoKcHJpbnQoZGltKGRmX2ltcGFjdCkpCmhlYWQoZGZfaW1wYWN0KQpgYGAKCiMjIERlc2NyaXB0aXZlIHN0YXRpc3RpY3MKYGBge3J9CgojIyBNYWtlIGNvdW50cyBwZXIgZ3JvdXAKZGZfc2FtcGxlX2NvdW50cyA9IGRmICU+JQogIGdyb3VwX2J5KFNBTVBMRSwgVFlQRTIsIElNUEFDVCkgJT4lCiAgc3VtbWFyaXplKE5fTVVUQVRJT05TID0gbigpKSAlPiUKICBmaWx0ZXIoTl9NVVRBVElPTlMgPj0gMjAsIE5fTVVUQVRJT05TIDw9IDIwMCkKCiMjIENhbGN1bGF0ZSBzdGF0cwpkZl9zYW1wbGVfc3RhdHMgPSBkZl9zYW1wbGVfY291bnRzICU+JQogIGdyb3VwX2J5KFRZUEUyLCBJTVBBQ1QpICU+JQogIHN1bW1hcml6ZSgKICAgIE1FRElBTiA9IG1lZGlhbihOX01VVEFUSU9OUyksCiAgICBVUFBFUiA9IHF1YW50aWxlKE5fTVVUQVRJT05TLCAuNzUpLAogICAgTE9XRVIgPSBxdWFudGlsZShOX01VVEFUSU9OUywgLjI1KSwKICAgIE1FQU4gPSBtZWFuKE5fTVVUQVRJT05TKSwKICAgIFNURCA9IHNkKE5fTVVUQVRJT05TKSwKICAgIE1JTiA9IG1pbihOX01VVEFUSU9OUyksCiAgICBNQVggPSBtYXgoTl9NVVRBVElPTlMpCiAgKQoKaGVhZChkZl9zYW1wbGVfY291bnRzKQpkZl9zYW1wbGVfc3RhdHMKYGBgCgojIFZpc3VhbGl6YXRpb25zClVzZSBjb3dwbG90IGZvciBuaWNlIHRoZW1lIGFuZCBzdWJwbG90dGluZwpodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvY293cGxvdC92aWduZXR0ZXMvaW50cm9kdWN0aW9uLmh0bWwKYGBge3J9CmltZ19kaXIgPSAnaW1nLycKYGBgCgoKIyMgUmFua2luZwpOT1RFOiBzaGFwZSA9ICc0JyBjb3JyZXNwb25kcyB0byBYJ3MKYGBge3J9CnggPSAwOihuX3NhbXBsZXMtMSkKeSA9IHVubmFtZShzYW1wbGVfY291bnRzKQoKcCA9IGdncGxvdChtYXBwaW5nID0gYWVzKHgsIHkpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSA0KSArCiAgZ2d0aXRsZSgnTXV0YXRpb25zIHBlciBJbmRpdmlkdWFsJykgKwogIHhsYWIoJ0luZGl2aWR1YWwgUmFuaycpICsKICB5bGFiKCdOdW1iZXIgb2YgTXV0YXRpb25zJykKCnAKZ2dzYXZlKHBhc3RlMChpbWdfZGlyLCAncmFua19yLnBuZycpKSAKCmBgYAoKIyMgQmFyIHBsb3QKYGBge3J9CnAgPSBnZ3Bsb3QoCiAgICBtYXBwaW5nID0gYWVzKG5hbWVzKGNsYXNzX2NvdW50cyksIHVubmFtZShjbGFzc19jb3VudHMpKQogICkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSByZXYobmFtZXMoY2xhc3NfY291bnRzKSkpICsKICBjb29yZF9mbGlwKCkKCnAKZ2dzYXZlKHBhc3RlMChpbWdfZGlyLCAnYmFyX3IucG5nJykpCgpgYGAKCiMjIFN1YnBsb3RzCmBgYHtyfQoKIyMgRmlyc3QgcGxvdAojIFJldXNlIGluc3RhbmNlIGZyb20gYWJvdmUKcDEgPSBwCgojIyBTZWNvbmQgcGxvdApuX2dlbmVzX3RvX3Bsb3QgPSA1CmdlbmVzID0gZGZfc3VtbWFyeSRHRU5FWzA6bl9nZW5lc190b19wbG90XQpjb3VudHMgPSBkZl9zdW1tYXJ5JE5fU0FNUExFU1swOm5fZ2VuZXNfdG9fcGxvdF0KcDIgPSBnZ3Bsb3QobWFwcGluZyA9IGFlcyhnZW5lcywgY291bnRzKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBnZW5lcykKICAKcF9ncmlkID0gcGxvdF9ncmlkKHAxLCBwMiwgbmNvbCA9IDIpCnNhdmVfcGxvdChwYXN0ZTAoaW1nX2RpciwgJ3N1YnBsb3Rfci5wbmcnKSwgcF9ncmlkLCBuY29sID0gMikKCnBfZ3JpZApgYGAKCiMjIENvdW50IHZzIGlkZW50aXR5CmBgYHtyfQoKIyMgTmV3IGRmJ3MgZm9yIHBsb3R0aW5nCmRmX2lkZW50aXR5ID0gZGZfc3VtbWFyeSAlPiUKICBhcnJhbmdlKC1OX01VVEFUSU9OUykKZGZfY291bnQgPSBkZgoKIyMgUGxvdCBwYXJhbXMKbl9nZW5lc190b19wbG90ID0gNQpnZW5lcyA9IGRmX2lkZW50aXR5JEdFTkVbMDpuX2dlbmVzX3RvX3Bsb3RdCmNvdW50cyA9IGRmX2lkZW50aXR5JE5fTVVUQVRJT05TWzA6bl9nZW5lc190b19wbG90XQoKIyMgSWRlbnRpdHkgcGxvdApwMSA9IGdncGxvdChtYXBwaW5nID0gYWVzKGdlbmVzLCBjb3VudHMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsgCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBnZW5lcykgKwogIGdndGl0bGUoJyMgTXV0YXRpb25zIC0gSWRlbnRpdHknKQoKIyMgQ291bnQgcGxvdApwMiA9IGdncGxvdChkZiwgYWVzKEdFTkUpKSArCiAgZ2VvbV9iYXIoKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBnZW5lcykgKyAKICBnZ3RpdGxlKCcjIE11dGF0aW9ucyAtIENvdW50JykKCnBfZ3JpZCA9IHBsb3RfZ3JpZChwMSwgcDIsIG5jb2wgPSAyKQpzYXZlX3Bsb3QocGFzdGUwKGltZ19kaXIsICdjb3VudF92c19pZGVudGl0eV9yLnBuZycpLCBwX2dyaWQsIG5jb2wgPSAyKQpwX2dyaWQKYGBgCgoKIyMgUGxvdCB3aXRoIGdyb3VwaW5nCmBgYHtyfQoKIyMgUGxvdCBwYXJhbXMKbl9nZW5lc190b19wbG90ID0gNQpnZW5lcyA9IGRmX3N1bW1hcnkkR0VORVswOm5fZ2VuZXNfdG9fcGxvdF0KCiMjIEZpbHRlciB0byByZWx2YW50IGRhdGEKZGZfcGx0ID0gZGZfaW1wYWN0ICU+JQogIGZpbHRlcihHRU5FICVpbiUgZ2VuZXMpCgojIyBSZW9yZGVyIGZvciBwbG90CiMgVGhpcyBpcyBhbiBhbHRlcm5hdGl2ZSB0byAKIyBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cz1nZW5lcykKZGZfcGx0JEdFTkUgPSBmYWN0b3IoZGZfcGx0JEdFTkUsIGxldmVscyA9IGdlbmVzKQoKcCA9IGdncGxvdChkYXRhID0gZGZfcGx0KSArCiAgZ2VvbV9iYXIoCiAgICBhZXMoeD1HRU5FLCB5PUNPVU5ULCBmaWxsPUlNUEFDVCksCiAgICBzdGF0ID0gJ2lkZW50aXR5JywKICAgIHBvc2l0aW9uID0gJ2RvZGdlJwogICkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKAogICAgdHlwZT0ncXVhbCcsCiAgICBwYWxldHRlID0gJ1NldDEnCiAgKSArCiAgZ2d0aXRsZSgnSW1wYWN0IG9mIE11dGF0aW9ucycpCgpwCmdnc2F2ZShwYXN0ZTAoaW1nX2RpciwgJ2dyb3VwX3IucG5nJyksIHApCmBgYAoKIyMgRmFjZXRlZCBwbG90CmBgYHtyfQoKIyMgR2V0IHNvbWUgc2FtcGxlcyBhbmQgZ2VuZXMKc2FtcGxlcyA9IG5hbWVzKHNhbXBsZV9jb3VudHNbMDoyXSkKZ2VuZXMgPSBjKCdQSUszQ0EnLCAnVFROJywgJ01VQzE2JykKCiMjIEZpbHRlciB0byByZWxldmFudCBkYXRhCmRmX3BsdCA9IGRmICU+JQogIGZpbHRlcihTQU1QTEUgJWluJSBzYW1wbGVzICYgR0VORSAlaW4lIGdlbmVzKQoKIyMgUmVvcmRlcgpkZl9wbHQkR0VORSA9IGZhY3RvcihkZl9wbHQkR0VORSwgbGV2ZWxzID0gZ2VuZXMpCmRmX3BsdCRTQU1QTEUgPSBmYWN0b3IoZGZfcGx0JFNBTVBMRSwgbGV2ZWxzID0gc2FtcGxlcykKCiMjIFBsb3QKcCA9IGdncGxvdChkZl9wbHQpICsKICBnZW9tX2JhcigKICAgIGFlcyh4PUdFTkUsIGZpbGw9SU1QQUNUKSwKICAgIHBvc2l0aW9uID0gJ2RvZGdlJwogICkgKyAKICBzY2FsZV9maWxsX2JyZXdlcigKICAgIHR5cGU9J3F1YWwnLAogICAgcGFsZXR0ZSA9ICdTZXQxJwogICkgKwogIGZhY2V0X3dyYXAoJ1NBTVBMRScpCgpwCmdnc2F2ZShwYXN0ZTAoaW1nX2RpciwgJ2ZhY2V0X3IucG5nJyksIHApCmBgYAoKCgojIyBCb3ggcGxvdApgYGB7cn0KCiMjIFNldCBvcmRlcgpkZl9zYW1wbGVfY291bnRzJFRZUEUyID0gZmFjdG9yKGRmX3NhbXBsZV9jb3VudHMkVFlQRTIsIGxldmVscyA9IGMoJ0lOREVMJywgJ1NOUCcpKQpkZl9zYW1wbGVfY291bnRzJElNUEFDVCA9IGZhY3RvcihkZl9zYW1wbGVfY291bnRzJElNUEFDVCwgbGV2ZWxzID0gYygKICAnSElHSCcsICdNT0RFUkFURScsICdMT1cnLCAnTU9ESUZJRVInKSkKCiMjIEJveCBwbG90CnAgPSBnZ3Bsb3QoZGZfc2FtcGxlX2NvdW50cywgYWVzKFRZUEUyLCBOX01VVEFUSU9OUywgZmlsbCA9IElNUEFDVCkpICsKICBnZW9tX2JveHBsb3QoKSArCiAgY29vcmRfZmxpcCgpICsKICBzY2FsZV9maWxsX2JyZXdlcigKICAgIHR5cGU9J3F1YWwnLAogICAgcGFsZXR0ZSA9ICdTZXQxJwogICkgKwogIGdndGl0bGUoJ011dGF0aW9ucyBQZXIgU2FtcGxlJykgKwogIHhsYWIoJ1R5cGUnKQoKcApnZ3NhdmUocGFzdGUwKGltZ19kaXIsICdib3hfZ3JvdXBfci5wbmcnKSwgcCkKYGBg